﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

namespace NearForums.DataAccess.Xml
{
    class ForumsDataAccess : BaseDataAccess, IForumsDataAccess
    {

        public ForumsDataAccess()
        {

        }

        public void Add(Forum f, int userId)
        {
            string filePath = GetDataPath(ForumsFile);
            XDocument doc = XDocument.Load(filePath);
            int count = (int)doc.Root.Attribute("Count");
            f.Id = ++count;

            XElement elem = new XElement("Forum",
                                new XElement("Id", f.Id),
                                new XElement("Name", f.Name),
                                new XElement("ShortName", f.ShortName),
                                new XElement("Description", f.Description),
                                new XElement("CategoryId", f.Category.Id),
                                new XElement("UserId", userId),
                                new XElement("CreationDate", ToString(DateTime.Now)),
                                new XElement("LastEditDate", null),
                                new XElement("LastEditUser", null),
                                new XElement("Active", true),
                                new XElement("TopicCount", f.TopicCount),
                                new XElement("MessageCount", f.MessageCount),
                                new XElement("Order", 0),
                                new XElement("ReadAccessGroupId", f.ReadAccessRole),
                                new XElement("PostAccessGroupId", f.PostAccessRole)
                                );

            doc.Root.Add(elem);
            doc.Root.SetAttributeValue("Count", count);
            doc.Save(filePath);
        }

        internal void AddTopic(int forumId, int count = 1)
        {
            string filePath = GetDataPath(ForumsFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from f in doc.Root.Elements("Forum")
                      where (int)f.Element("Id") == forumId
                      select f;

            var forumElem = qry.SingleOrDefault();
            if (forumElem != null)
            {
                forumElem.SetElementValue("TopicCount", (int)forumElem.Element("TopicCount") + count);
            }
            doc.Save(filePath);
        }

        internal void AddMessage(int forumId, int count = 1)
        {
            string filePath = GetDataPath(ForumsFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from f in doc.Root.Elements("Forum")
                      where (int)f.Element("Id") == forumId
                      select f;

            var forumElem = qry.SingleOrDefault();
            if (forumElem != null)
            {
                forumElem.SetElementValue("MessageCount", (int)forumElem.Element("MessageCount") + count);
            }
            doc.Save(filePath);
        }

        public bool Delete(string forum)
        {
            string filePath = GetDataPath(ForumsFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from f in doc.Root.Elements("Forum")
                      where (string)f.Element("ShortName") == forum
                      select f;

            XElement elem = qry.SingleOrDefault();
            if (elem != null)
            {
                elem.SetElementValue("Active", false);
                doc.Save(filePath);
                return true;
            }

            return false;
        }

        public void Edit(Forum forum, int userId)
        {
            string filePath = GetDataPath(ForumsFile);
            XDocument doc = XDocument.Load(filePath);
            var qry = from f in doc.Root.Elements("Forum")
                      where (string)f.Element("ShortName") == forum.ShortName
                      select f;

            XElement elem = qry.SingleOrDefault();
            if (elem != null)
            {
                elem.SetElementValue("Name", forum.Name);
                elem.SetElementValue("ShortName", forum.ShortName);
                elem.SetElementValue("Description", forum.Description);
                elem.SetElementValue("CategoryId", forum.Category.Id);
                elem.SetElementValue("UserId", userId);
                elem.SetElementValue("LastEditDate", ToString(DateTime.Now));
                elem.SetElementValue("LastEditUser", userId);
                elem.SetElementValue("Active", true);
                elem.SetElementValue("ReadAccessGroupId", forum.ReadAccessRole);
                elem.SetElementValue("PostAccessGroupId", forum.PostAccessRole);
                doc.Save(filePath);
            }

        }

        public string GetAvailableShortName(string shortName)
        {
            // must be unique
            Forum forum = GetByShortName(shortName);
            if (forum == null)
                return shortName;
            else
                return shortName + "-" + DateTime.Now.Ticks.ToString();
        }

        public Forum GetByShortName(string shortName)
        {
            XDocument doc = XDocument.Load(GetDataPath(ForumsFile));
            var qry = from f in doc.Root.Elements("Forum")
                      where (string)f.Element("ShortName") == shortName
                      select f;

            var elem = qry.SingleOrDefault();

            Forum forum = null;
            if (elem != null)
            {
                forum = GetForumFromElement(elem);
                forum.Category = GetCategory(forum.Category.Id);
            }

            return forum;
        }

        public List<ForumCategory> GetCategories()
        {
            XDocument doc = XDocument.Load(GetDataPath(CategoriesFile));
            var qry = from c in doc.Root.Elements("Category")
                      orderby (int)c.Attribute("Order")
                      select new ForumCategory
                        {
                            Id = (int)c.Attribute("Id"),
                            Name = (string)c.Attribute("Name")
                        };
            List<ForumCategory> categories = qry.ToList();
            foreach (ForumCategory category in categories)
            {
                category.Forums = GetForumsForCategory(category);
            }
            return categories;
        }

        public List<ForumCategory> GetList(UserRole? role)
        {
            XDocument doc = XDocument.Load(GetDataPath(CategoriesFile));
            var qry = from c in doc.Root.Elements("Category")
                      select new ForumCategory
                      {
                          Id = (int)c.Attribute("Id"),
                          Name = (string)c.Attribute("Name")
                      };
            List<ForumCategory> categories = qry.ToList();

            int roleValue = role.HasValue ? (int)role.Value : 0;
            foreach (ForumCategory category in categories)
            {
                List<Forum> forums = GetForumsForCategory(category);
                category.Forums = new List<Forum>();
                foreach (Forum forum in forums)
                {
                    int forumRoleValue = forum.ReadAccessRole.HasValue ? (int)forum.ReadAccessRole.Value : 0;
                    if (roleValue >= forumRoleValue)
                    {
                        category.Forums.Add(forum);
                    }
                }
            }
            return categories;
        }

        private List<Forum> GetForumsForCategory(ForumCategory category)
        {
            int categoryId = category.Id;
            XDocument doc = XDocument.Load(GetDataPath(ForumsFile));
            var qry = from f in doc.Root.Elements("Forum")
                      where (int)f.Element("CategoryId") == categoryId
                      && (bool)f.Element("Active") == true
                      select f;

            List<Forum> forums = new List<Forum>();
            foreach (var elem in qry)
            {
                Forum forum = GetForumFromElement(elem);
                forum.Category = category;
                forums.Add(forum);
            }
            return forums;
        }

        private ForumCategory GetCategory(int categoryId)
        {
            XDocument doc = XDocument.Load(GetDataPath(CategoriesFile));
            var qry = from c in doc.Root.Elements("Category")
                      where (int)c.Attribute("Id") == categoryId
                      select new ForumCategory
                      {
                          Id = categoryId,
                          Name = (string)c.Attribute("Name")
                      };
            return qry.SingleOrDefault();

        }

        private Forum GetForumFromElement(XElement f)
        {
            Forum forum = new Forum();
            forum.Id = (int)f.Element("Id");
            forum.Name = (string)f.Element("Name");
            forum.ShortName = (string)f.Element("ShortName");
            forum.Description = (string)f.Element("Description");
            // get category from category id
            forum.Category = new ForumCategory { Id = (int)f.Element("CategoryId") };
            forum.TopicCount = (int)f.Element("TopicCount");
            forum.MessageCount = (int)f.Element("MessageCount");
            forum.ReadAccessRole = ((string)f.Element("ReadAccessRole") == null ? null : (UserRole?)Enum.Parse(typeof(UserRole), (string)f.Element("ReadAccessGroupId")));
            forum.PostAccessRole = (UserRole)Enum.Parse(typeof(UserRole), (string)f.Element("PostAccessGroupId"));
            return forum;
        }
    }
}
